﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Sinacor.OMM.Util;
using System.Data;
using System.Diagnostics;
using Sinacor.OMM.Logging;
using System.Data.Common;
using PSE.Framework.Threading;

namespace Sinacor.OMM.ConnectionManager.DataBase
{
    public class DataBaseCommunicator : IDisposable
    {
        private const string _traceFolderName = "ConnectionManager";
        
        const int TIME_TO_RETRY_DATABASE_CONNECTION = 10000;
        const int TIME_TO_CHECK_DATABASE_CONNECTION = 1000;

        TraceWriter _traceWriter;
        IDataBase _database;
        string _logIdentifier;
        bool _keepRunning;

        private long _connectionTimes = 0;
        private long _connectionWithError = 0;

        private string _traceFileName;

        public long ConnectionTimes
        {
            get
            {
                return _connectionTimes;
            }
        }

        public long ConnectionWithError
        {
            get
            {
                return _connectionWithError;
            }
        }

        public DataBaseCommunicator(IDataBase database, string logIdentifier)
        {
            _traceFileName = "DataBaseCommunicator " + logIdentifier;
            _traceWriter = Tracer.GetInstance(_traceFolderName, _traceFileName);

            if (database == null)
                throw new ArgumentNullException("IDatabase Não Pode Ser Nulo - " + logIdentifier);

            _database = database;
            _logIdentifier = logIdentifier;

            Thread thr = new Thread(new ThreadStart(Run));
            thr.Name = _logIdentifier;
            thr.Start();
        }

        private void Run()
        {
            _keepRunning = true;

            _traceWriter.Trace("Iniciou a Thread que vai manter o banco conectado independente do tipo de desastre...");
            while (_keepRunning)
            {
                try
                {
                    if (_database != null && (!_database.Connected))
                    {
                        _traceWriter.Trace("Não está conectado ao database.");
                        try
                        {
                            _traceWriter.Trace("Tentará conectar no Banco de dados pela [" + (_connectionTimes + _connectionWithError + 1) + "] vez");
                            if (_database != null && !_database.Connect())
                            {
                                _connectionWithError++;
                                
                                _traceWriter.Trace("ERRO ao conectar no banco de dados");                                

                                _traceWriter.Trace("Aguardará " + TIME_TO_RETRY_DATABASE_CONNECTION + "ms para tentar se reconectar ao banco.", TraceType.Optional);
                                Sleeper.Sleep(TIME_TO_RETRY_DATABASE_CONNECTION, IsRunning);
                            }
                            else
                            {
                                _traceWriter.Trace("Conseguiu conectar no banco");
                                _connectionTimes++;
                            }
                        }
                        catch (Exception ex)
                        {
                            _connectionWithError++;

                            _traceWriter.Trace("ERRO ao conectar no banco de dados");
                            _traceWriter.Trace(ex.Message);
                            _traceWriter.Trace(ex.StackTrace); 
                            
                            EventLog.WriteEntry(LogSources.MSMQConnectionManager, "Erro ao executar o Connection Manager [" + ex.Message + " " + ex.StackTrace + "]", EventLogEntryType.Warning);

                            _traceWriter.Trace("Aguardará " + TIME_TO_RETRY_DATABASE_CONNECTION + "ms para tentar se reconectar ao banco.");
                            Sleeper.Sleep(TIME_TO_RETRY_DATABASE_CONNECTION, IsRunning);
                        }
                    }
                    else
                    {

                        _traceWriter.Trace("Aguardará " + TIME_TO_CHECK_DATABASE_CONNECTION + "ms verificar se a conexão com o banco de dados permanece OK", TraceType.Optional);

                        //Não precisa logar, está tudo bem é só o sleep para checar depois se está tudo bem novamnte....
                        Sleeper.Sleep(TIME_TO_CHECK_DATABASE_CONNECTION, IsRunning);
                    }
                }
                catch (Exception ex)
                {
                    _traceWriter.Trace("Erro de causa desconhecida...");
                    _traceWriter.Trace(ex.Message);
                    _traceWriter.Trace(ex.StackTrace);
                    
                    EventLog.WriteEntry(LogSources.MSMQConnectionManager, "Erro ao executar o Connection Manager [" + ex.Message + " " + ex.StackTrace + "]", EventLogEntryType.Warning);

                    _traceWriter.Trace("Aguardará " + TIME_TO_CHECK_DATABASE_CONNECTION + "ms verificar se a conexão com o banco de dados permanece OK");
                    Sleeper.Sleep(TIME_TO_RETRY_DATABASE_CONNECTION, IsRunning);
                }
            }
        }

        public bool ExecuteNonQuery(string command)
        {

            bool ret = false;
            try
            {
                if (_keepRunning && _database != null && _database.Connected)
                {
                    if (_database.ExecuteNonQuery(command))
                    {
                        ret = true;
                        _traceWriter.Trace("SUCESSO: ExecuteNonQuery(command) retornou TRUE", TraceType.Optional);
                        _traceWriter.Trace("SUCESSO: ExecuteNonQuery(command) retornou TRUE - Command:[" + command + "]", TraceType.Optional);
                    }
                    else
                    {
                        ret = false;
                        _traceWriter.Trace("ExecuteNonQuery(command) retornou FALSE");
                        _traceWriter.Trace("ExecuteNonQuery(command) retornou FALSE - Command:[" + command + "]");
                    }
                }
                else
                {
                    ret = false;
                    _traceWriter.Trace("DESCONECTADO - Não executou ExecuteNonQuery(command)");
                    _traceWriter.Trace("DESCONECTADO - Não executou Command [" + command + "]");
                }
            }
            catch (Exception ex)
            {
                ret = false;

                _traceWriter.Trace("Erro ao executar ExecuteNonQuery(command)");
                _traceWriter.Trace("Erro ao executar Command [" + command + "]");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);
                
                EventLog.WriteEntry(LogSources.MSMQConnectionManager, "Erro ao executar ExecuteNonQuery [" + ex.Message + " " + ex.StackTrace + "]", EventLogEntryType.Warning);
                throw ex;
            }

            return ret;
        }

        public bool ExecuteNonQuery(DbTransaction transaction, string command)
        {

            bool ret = false;
            try
            {
                if (_keepRunning && _database != null && _database.Connected)
                {
                    if (_database.ExecuteNonQuery(transaction, command))
                    {
                        ret = true;
                        _traceWriter.Trace("SUCESSO: ExecuteNonQuery(command) retornou TRUE", TraceType.Optional);
                        _traceWriter.Trace("SUCESSO: ExecuteNonQuery(command) retornou TRUE - Command:[" + command + "]", TraceType.Optional);
                    }
                    else
                    {
                        ret = false;
                        _traceWriter.Trace("ExecuteNonQuery(command) retornou FALSE");
                        _traceWriter.Trace("ExecuteNonQuery(command) retornou FALSE - Command:[" + command + "]");
                    }
                }
                else
                {
                    ret = false;
                    _traceWriter.Trace("DESCONECTADO - Não executou ExecuteNonQuery(command)");
                    _traceWriter.Trace("DESCONECTADO - Não executou Command [" + command + "]");
                }
            }
            catch (Exception ex)
            {
                ret = false;

                _traceWriter.Trace("Erro ao executar ExecuteNonQuery(command)");
                _traceWriter.Trace("Erro ao executar Command [" + command + "]");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);

                EventLog.WriteEntry(LogSources.MSMQConnectionManager, "Erro ao executar ExecuteNonQuery [" + ex.Message + " " + ex.StackTrace + "]", EventLogEntryType.Warning);
                throw ex;
            }

            return ret;
        }

        public bool ExecuteNonQuerySP(DbTransaction transaction, string storedProcedureName, params object[] parameterValues)
        {

            bool ret = false;
            try
            {
                if (_keepRunning && _database != null && _database.Connected)
                {
                    if (_database.ExecuteNonQuerySP(transaction, storedProcedureName, parameterValues))
                    {
                        ret = true;
                        _traceWriter.Trace("SUCESSO: ExecuteNonQuery(command) retornou TRUE", TraceType.Optional);
                        _traceWriter.Trace("SUCESSO: ExecuteNonQuery(command) retornou TRUE - Command:[" + storedProcedureName + "]", TraceType.Optional);
                    }
                    else
                    {
                        ret = false;
                        _traceWriter.Trace("ExecuteNonQuery(command) retornou FALSE");
                        _traceWriter.Trace("ExecuteNonQuery(command) retornou FALSE - Command:[" + storedProcedureName + "]");
                    }
                }
                else
                {
                    ret = false;
                    _traceWriter.Trace("DESCONECTADO - Não executou ExecuteNonQuery(command)");
                    _traceWriter.Trace("DESCONECTADO - Não executou Command [" + storedProcedureName + "]");
                }
            }
            catch (Exception ex)
            {
                ret = false;

                _traceWriter.Trace("Erro ao executar ExecuteNonQuery(command)");
                _traceWriter.Trace("Erro ao executar Command [" + storedProcedureName + "]");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);

                EventLog.WriteEntry(LogSources.MSMQConnectionManager, "Erro ao executar ExecuteNonQuery [" + ex.Message + " " + ex.StackTrace + "]", EventLogEntryType.Warning);
                throw ex;
            }

            return ret;
        }

        public IDataReader ExecuteReader(string command)
        {

            IDataReader ret = null;
            try
            {
                if (_keepRunning && _database != null && _database.Connected)
                {
                    ret = _database.ExecuteReader(command);

                    if (ret != null)
                    {
                        _traceWriter.Trace("SUCESSO ExecuteReader(command) retornou um objeto com [" + ret.FieldCount + "] campos e [" + ret.RecordsAffected + "] RecordsAffected", TraceType.Optional);
                    }
                    else
                    {
                        _traceWriter.Trace("ExecuteReader(command) retornou null");
                        _traceWriter.Trace("ExecuteReader(command) retornou null - Command:[" + command + "]");
                    }
                }
                else
                {
                    _traceWriter.Trace("DESCONECTADO - Não executou ExecuteReader(command)");
                    _traceWriter.Trace("DESCONECTADO - Não executou Command [" + command + "]");
                }
            }
            catch (Exception ex)
            {
                _traceWriter.Trace("Erro ao executar ExecuteReader(command)");
                _traceWriter.Trace("Erro ao executar Command [" + command + "]");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);
                
                EventLog.WriteEntry(LogSources.MSMQConnectionManager, "Erro ao executar ExecuteReader [" + ex.Message + " " + ex.StackTrace + "]", EventLogEntryType.Warning);
                
            }

            return ret;
        }

        public object ExecuteScalar(string command)
        {
            object ret = null;

            try
            {
                if (_keepRunning && _database != null && _database.Connected)
                {
                    ret = _database.ExecuteScalar(command);

                    if (ret != null)
                    {
                        _traceWriter.Trace("SUCESSO ExecuteScalar(command) retornou um objeto", TraceType.Optional);
                    }
                    else
                    {
                        _traceWriter.Trace("ExecuteScalar(command) retornou null");
                        _traceWriter.Trace("ExecuteScalar(command) retornou null - Command:[" + command + "]");
                    }
                }
                else
                {
                    _traceWriter.Trace("DESCONECTADO - Não executou ExecuteScalar(command)");
                    _traceWriter.Trace("DESCONECTADO - Não executou Command [" + command + "]");
                }
            }
            catch (Exception ex)
            {
                _traceWriter.Trace("Erro ao executar ExecuteScalar(command)");
                _traceWriter.Trace("Erro ao executar Command [" + command + "]");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);
                
                EventLog.WriteEntry(LogSources.MSMQConnectionManager, "Erro ao executar ExecuteScalar [" + ex.Message + " " + ex.StackTrace + "]", EventLogEntryType.Warning);
            }

            return ret;
        }

        private bool IsRunning()
        {
            return _keepRunning;
        }

        public void Dispose()
        {
            _keepRunning = false;

            _traceWriter.Trace("Disposing object");

            if (_database != null)
            {
                _database.Disconnect();
                _database.Close();
            }
            _traceWriter.Trace("Object Disposed");

            Tracer.RemoveInstance(_traceFolderName, _traceFileName);
        }

        public DbTransaction BeginTransaction()
        {
            bool ret = false;
            DbTransaction transaction = null;

            try
            {
                if (_database != null && _database.Connected)
                {
                    transaction = _database.BeginTransaction();
                    ret = true;
                    _traceWriter.Trace("SUCESSO executou BeginTransaction()!", TraceType.Optional);
                }
                else
                {
                    _traceWriter.Trace("DESCONECTADO - Não executou BeginTransaction()");
                }
            }
            catch (Exception ex)
            {
                _traceWriter.Trace("Erro ao executar BeginTransaction()");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);
            }
            return transaction;
        }

        public bool RollBack()
        {
            bool ret = false;
            try
            {
                if (_database != null && _database.Connected)
                {
                    _database.RollBack();
                    ret = true;
                    _traceWriter.Trace("SUCESSO executou RollBack()!", TraceType.Optional);
                }
                else
                {
                    _traceWriter.Trace("DESCONECTADO - Não executou RollBack()");
                }
            }
            catch (Exception ex)
            {
                _traceWriter.Trace("Erro ao executar RollBack()");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);
            }
            return ret;
        }

        public bool Commit()
        {
            bool ret = false;
            try
            {
                if (_database != null && _database.Connected)
                {
                    _database.Commit();
                    ret = true;
                    _traceWriter.Trace("SUCESSO executou Commit()!", TraceType.Optional);
                }
                else
                {
                    _traceWriter.Trace("DESCONECTADO - Não executou Commit()");
                }
            }
            catch (Exception ex)
            {
                _traceWriter.Trace("Erro ao executar Commit()");
                _traceWriter.Trace(ex.Message);
                _traceWriter.Trace(ex.StackTrace);
            }
            return ret;
        }
    }
}
